<#--SERVICE-->
<#-- 传入参数 Func func, DataTable table, Form form--> 
package ${func.javaPkg}.service;

import java.util.ArrayList;
import java.math.BigDecimal;
import java.time.*;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import com.vecspace.utils.Assert;
import com.vecspace.utils.Convert;
import com.vecspace.utils.DateTimeUtil;
import com.vecspace.utils.NumberUtil;
import com.vecspace.utils.TJSON;
import com.vecspace.exception.HException;
import com.vecspace.exception.DBExceptionManager;
import com.vecspace.database.model.PojoManager;
import com.vecspace.data.model.FilterColumn;
import com.vecspace.data.model.ListExBO;
import com.vecspace.database.model.QueryResultAggregation;
import com.vecspace.security.model.LoginContext;
import com.vecspace.constant.AuthTag;
import com.vecspace.framework.sysdata.service.SysDataService;
<#if table.isLockByRow>
import com.vecspace.data.model.DataLockState;
</#if>
<#if foreignTable??>
import ${func.javaPkg}.pojo.${foreignTable.className};
</#if>
import ${func.javaPkg}.pojo.${table.className};
import ${func.javaPkg}.pojo.${table.className}Ex;
import ${func.javaPkg}.pojo.${table.className}Stats;
import ${func.javaPkg}.dao.${table.className}Dao;

/**功能描述 ${table.TableDesc!}的增、删、改查等业务操作的service类
 * @template dayu-code-builder
 * @author ${author}
 * @reviewer 
 * @createTime ${nowTime}
 * @version 1.0
 */
@Service("${sysName}_${table.beanName}Service")
public class ${table.className}Service {
    /** 根据ID查询 ${table.tableCaption!}
     * @param ${table.idVarName}   主键值
     * @return ${table.className}
     * @throws Exception
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public ${table.className} get${table.className}(${table.idPropJavaType} ${table.idVarName}) throws Exception{
        ${table.className} ${table.beanName} = ${table.beanName}Dao.get${table.className}(${table.idVarName});
        return ${table.beanName};
    }


<#if parentColumn??><#-- 本表自身的上级字段，只有this.isTree==true时有效 -->
    /**根据父节点id获取子节点
     *
     * @param parentId
     * @return
     * @throws Exception
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public List<${table.className}> getChild${table.className}List(<#if foreignColumn??>${foreignColumn.javaType} ${foreignColumn.varName}, </#if>${parentColumn.javaType} ${parentColumn.varName}) throws Exception {
        List<${table.className}> ${table.beanName}List = ${table.beanName}Dao.getChild${table.className}List(<#if foreignColumn??>${foreignColumn.varName},</#if>${parentColumn.varName});
        return ${table.beanName}List;
    }

    /**根据 ${parentColumn.columnCaption} 获取${table.tableCaption}记录条数
     *
     * @param ${parentColumn.varName}  ${parentColumn.columnCaption}
     * @return
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public int get${table.className}CountBy${parentColumn.propNameTrimId}(${parentColumn.javaType} ${parentColumn.varName}) throws Exception {
        int rcount = ${table.beanName}Dao.get${table.className}CountBy${parentColumn.propNameTrimId}(${parentColumn.varName});
        return rcount;
    }
</#if>

<#if foreignColumn??><#-- 本表(this.table)中，关联目录表(或者主表)的字段 -->
    /**根据 ${foreignColumn.columnCaption} 获取${table.tableCaption}列表
     *
     * @param ${foreignColumn.varName} ${foreignColumn.columnCaption}
     * @return
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public List<${table.className}> get${table.className}ListBy${foreignTable.className}(${foreignColumn.javaType} ${foreignColumn.varName}) throws Exception {
        List<${table.className}> ${table.beanName}List = ${table.beanName}Dao.get${table.className}ListBy${foreignColumn.propNameTrimId}(${foreignColumn.varName});
        return ${table.beanName}List;
    }

    /**根据 ${foreignColumn.columnCaption} 获取${table.tableCaption}记录条数
     *
     * @param ${foreignColumn.varName}  ${foreignColumn.columnCaption}
     * @return
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public int get${table.className}CountBy${foreignColumn.propNameTrimId}(${foreignColumn.javaType} ${foreignColumn.varName}) throws Exception {
        int rcount = ${table.beanName}Dao.get${table.className}CountBy${foreignColumn.propNameTrimId}(${foreignColumn.varName});
        return rcount;
    }
</#if>

<#if updateCellValueEnable==true>
    /**根据属性名获取字段值
     * @param ${table.idVarName} 记录id
     * @param propName 属性名
     * @return
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public Object getValueByProp(${table.idPropJavaType} ${table.idVarName}, String propName) throws Exception {
    	return ${table.beanName}Dao.getValueByProp(${table.idVarName}, propName);
    }

    /**根据id更新指定字段的值（根据propName从pojo获取字段名）
     * @param ${table.idVarName} 记录id
     * @param propName 属性名
     * @param value 属性值
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public void updateValueByProp(${table.idPropJavaType} ${table.idVarName}, String propName, Object value) throws Exception {
<#if table.isLockByRow || table.isLockByMaster>
        ${table.className} ${table.beanName} = ${table.beanName}Dao.get${table.className}(${table.idVarName});
        if(${table.beanName} == null)return;
        checkNotLocked(${table.beanName});
</#if>
    	Object dbVal = value;
    	String[] propNames = new String[]{ ${table.columnVarNamesStr!''} };
    	String[] datePropNames = new String[]{ ${table.dateColumnVarNamesStr!''} };
    	String[] numberPropNames = new String[]{ ${table.numberColumnVarNamesStr!''} };
    	if(ArrayUtils.indexOf(propNames, propName) < 0) throw new HException("属性名不正确:"+propName);
        if(ArrayUtils.indexOf(datePropNames, propName) >= 0 && value != null){
            if(value.getClass().equals(String.class)){
                dbVal = TJSON.convertToObject(LocalDateTime.class, String.format("\"{0}\"", value));
            }
        }
        if(ArrayUtils.indexOf(numberPropNames, propName) >= 0 && value != null){
            if(value.getClass().equals(String.class)){
                if(StringUtils.isNotBlank(value.toString())){
                    dbVal = new BigDecimal(value.toString());
                }else{
                    dbVal = null;
                }
            }
        }
    	${table.beanName}Dao.updateValueByProp(${table.idVarName}, propName, dbVal);
    }
</#if>

    /***将页面上传入的对象转为持久化对象（如果页面对象是一个新对象，则insert;否则从数据库库查询出已有的对象item2，将传入的值赋值给item2，然后将item2 update到数据库）
     * 根据页面上放置的属性个数，需要对下面的属性设置进行调整。
     * 对于无法在页面上进行修改的属性，不要进行设值。
     * @param item item是由页面传递过来的对象，item中的属性并不完整，对于在页面上没有展示的属性，item中一律会是空值，而数据库中这些属性可以是有值的
     * @return ${table.className}
     * @throws Exception
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public ${table.className} saveView${table.className}(${table.className} item) throws Exception {
        if (item == null){
            return null;
        }
        /*** 下面开始对item对象的页面传值进行校验，如必填项，格式等 ***/
        //Assert.throwz(StringUtils.isNotBlank() , "条件不满足时抛出异常");
        /*** 结束校验 ***/
        
        ${table.className} item2;
<#if table.idPropJavaType=="String">
        if (!StringUtils.isEmpty(item.get${table.idPropName}())) {
<#else>
        if (NumberUtil.isNotNullZero(item.get${table.idPropName}())) {
</#if>
            /**从数据库中取出原始对象，将从页面传过来的新值赋值给原始对象，并将原始对象再保存到数据库中 **/
            item2 = get${table.className}(item.get${table.idPropName}());
            if (item2 != null) {
                /*** 将页面上的新值逐一赋值，不在页面上的属性不要进行赋值 ***/
<#list columns as column>
    <#if column.isKeyColumn == true || column.jsonIgnore == true>
    <#else>
                item2.set${column.propName}(item.get${column.propName}());//更新${column.columnCaption}
    </#if>
</#list>
                /**end**/
            } else {
                item2 = item;
            }
        }else{ //item是新增加的对象
            item2 = item;
        }
        save${table.className}(item2);
        return item2;
    }

    /** 插入或修改 ${table.TableDesc!} ，item中的所有属性都会更新到数据库中（包括属性值为null的，也会将null更新到数据库，覆盖之前的值）。
     * @param item    要保存的对象
     * @return void
     * @throws Exception
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public void save${table.className}(${table.className} item) throws Exception{
        if (item == null){
            return;
        }
        /*** 下面开始对item对象进行校验，如逻辑关系等 ***/
<#if foreignColumn??>
  <#if foreignColumn.javaType == 'String'>
        Assert.throwz(StringUtils.isNotBlank(item.${foreignColumn.varName}), "${foreignColumn.varName}不能为空！");
  <#else>
        Assert.throwz(item.${foreignColumn.varName} != null && item.${foreignColumn.varName} != 0, "${foreignColumn.varName}不能为空！");
  </#if>
</#if>
<#if table.isLockByRow || table.isLockByMaster>
        checkNotLocked(item);
</#if>
<#if parentColumn??>
        if(item.get${parentColumn.propName}() == null || item.get${parentColumn.propName}().longValue() ==0){
            item.set${parentColumn.propName}(-1L);
        }
</#if>
        /*** 结束校验 ***/
        LocalDateTime dtime = LocalDateTime.now();
<#if table.idPropJavaType=="String">
        if (StringUtils.isBlank(item.get${table.idPropName}())) {
<#else>
        if (NumberUtil.isNullOrZero(item.get${table.idPropName}())) {
</#if>
            item.set${table.idPropName}(null);
<#list columns as column>
  <#if column.varName=="createTime" || column.varName=="recordCreateTime" || column.varName=="updateTime" || column.varName=="recordUpdateTime">
            item.set${column.propName}(dtime);  //创建时间
  <#elseif column.varName=="recordCreateUser" || column.varName=="createUser" || column.varName=="createUserId" || column.varName=="recordUpdateUser" || column.varName=="updateUser"  || column.varName=="updateUserId">
          <#if "String" == column.javaType>
            String loginId = LoginContext.getLoginId();//创建人
            item.set${column.propName}(loginId);
          <#else>
            Long loginUserId = LoginContext.getUserId();//创建人
            item.set${column.propName}(loginUserId);
          </#if>
  </#if>
</#list>
        } else {
<#list columns as column>
  <#if column.varName=="updateTime" || column.varName=="recordUpdateTime">
            item.set${column.propName}(date);//设置更新时间
  <#elseif column.varName=="recordUpdateUser" || column.varName=="updateUser"  || column.varName=="updateUserId">
          <#if "String" == column.javaType>
            String loginId = LoginContext.getLoginId();//设置更新人
            item.set${column.propName}(loginId);
          <#else>
            Long loginUserId = LoginContext.getUserId();//设置更新人
            item.set${column.propName}(loginUserId);
          </#if>
  </#if>
</#list>
        }
        try{
            ${table.beanName}Dao.save${table.className}(item);
        }catch(Exception ex){
            //识别并转换数据库约束错误
            Exception ex2 = DBExceptionManager.parseException(ex);
            throw ex2;
        }
    }

<#if isTree==true && parentColumn??>
    /**更改 自己所属的父节点
     *
     * @param ${table.idVarName} ${table.tableName}.${table.idVarName}
     * @param ${parentColumn.varName}New 新的 ${parentColumn.columnCaption}
     * @throws Exception
     * @author ${author}
     * @reviewer
     * @createTime ${nowTime}
     */
    public void change${parentColumn.propNameTrimId}(${table.idPropJavaType} ${table.idVarName}, ${parentColumn.javaType} ${parentColumn.varName}New) throws Exception {
<#if table.isLockByRow || table.isLockByMaster>
        ${table.className} ${table.beanName} = ${table.beanName}Dao.get${table.className}(${table.idVarName});
        if(${table.beanName} == null)return;
        checkNotLocked(${table.beanName});
</#if>
        ${table.beanName}Dao.change${parentColumn.propNameTrimId}(${table.idVarName}, ${parentColumn.varName}New);
    }
</#if>

<#if table.isLockByRow>
    /**修改数据锁定状态
     * @param id 记录id，另-1表示对整表进行锁定
     * @param lockState
     * @param authTag 权限类型，参考 AuthTag
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     **/
	public void changeLockState(${table.idPropJavaType} id, int lockState, int authTag) throws Exception {
        Assert.throwz(DataLockState.isValidState(lockState), "不是有效的锁定状态："+lockState);
        //权限验证，是否有选项更改锁定状态
        DataLockState.checkAuth(lockState, authTag);
        if(id != null && id > 0) {
            ${table.beanName}Dao.updateValueByProp(id, "rowLock", lockState);
        }
    }
</#if>

<#if table.isLockByMaster>
    /**<#if foreignColumn??>修改数据被主数据的锁定状态<#else>修改整表被锁定的状态</#if>
     <#if foreignColumn??>* @param ${foreignColumn.varName} 主数据记录id，另-1表示对${table.className}整表进行锁定</#if>
     * @param lockState
     * @param authTag 权限类型，参考 AuthTag
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     **/
	public void changeMasterLockState(<#if foreignColumn??>${foreignColumn.javaType} ${foreignColumn.varName}, </#if>int lockState, int authTag) throws Exception {
        Assert.throwz(DataLockState.isValidState(lockState), "不是有效的锁定状态："+lockState);
        //权限验证，是否有选项更改锁定状态
        DataLockState.checkAuth(lockState, authTag);
<#if foreignColumn??>
        if(${foreignColumn.varName} == null || ${foreignColumn.varName} <= 0) {
        	${foreignColumn.varName} = -1L;
        }
        if(${foreignColumn.varName} == -1) {
            //对整表进行锁定
            sysDataService.changeLockState(PojoManager.getTableName(${table.className}.class), "", -1L, null, lockState);
        }else {
            //只锁定指定 ${foreignColumn.varName} 的数据
            sysDataService.changeLockState(PojoManager.getTableName(${foreignTable.className}.class), "", ${foreignColumn.varName}, PojoManager.getTableName(${table.className}.class), lockState);
        }
<#else>
        //对整表进行锁定
        sysDataService.changeLockState(PojoManager.getTableName(${table.className}.class), "", -1L, null, lockState);
</#if>
    }
</#if>

<#if table.isLockByRow || table.isLockByMaster>
    /**1、自身处于锁定或者归档状态
     * 2、或者受主数据锁定（由表tm_sys_data_lock记录），或者全表被锁定
     * 3、不通过主数据自身的row_lock标记影响（因为主数据自身被锁定，不意味着所有的子表都被锁定）
     * @param ${table.beanName}
     * @throws Exception 
     */
    private void checkNotLocked(${table.className} ${table.beanName}) throws Exception {
    	
        if(DataLockState.isLockOrArchived(${table.beanName}.getRowLock())) {
        	throw new HException("本条记录处于锁定状态，不能编辑和删除");
        }
      <#if table.isLockByMaster>
        <#if foreignColumn??>
        int masterLockState = sysDataService.getLockState(PojoManager.getTableName(${foreignTable.className}.class), null, ${table.beanName}.get${foreignColumn.propName}(), PojoManager.getTableName(${table.className}.class));
        if(DataLockState.isLockOrArchived(masterLockState)) {
        	throw new HException("本条记录被主数据锁定，不能编辑和删除");
        }
        <#else>
        int tableLockState = sysDataService.getLockState(PojoManager.getTableName(${table.className}.class), null, -1L, null);
        if(DataLockState.isLockOrArchived(tableLockState)) {
        	throw new HException("本表处于锁定状态，不能编辑和删除");
        }
        </#if>
      </#if>
    }
</#if>

    /** 根据ID删除 ${table.TableDesc!}
     * @param ${table.idVarName}    主键值
     * @return void
     * @throws Exception
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public void delete${table.className}(${table.idPropJavaType} ${table.idVarName}) throws Exception{
<#if table.isLockByRow || table.isLockByMaster>
        ${table.className} ${table.beanName} = ${table.beanName}Dao.get${table.className}(${table.idVarName});
        if(${table.beanName} == null)return;
        checkNotLocked(${table.beanName});
</#if>
<#if parentColumn??>
        int childCount = ${table.beanName}Dao.get${table.className}CountBy${parentColumn.propNameTrimId}(${table.idVarName});
        if(childCount>0)throw new HException("还存在子节点，不能删除本节点");
</#if>
        ${table.beanName}Dao.checkReferenced("${table.tableName}", ${table.idVarName});
        ${table.beanName}Dao.delete${table.className}(${table.idVarName});
    }

    /**根据查询条件获取查询结果（分页查询），同时进行聚合统计查询
     * 
     <#if foreignColumn??>* @param ${foreignColumn.varName} ${foreignColumn.caption!} </#if>
     <#if useRecurseQuery==true>* @param recursion 是否递归查询所有下级数据 </#if>
     * @param filterColumns 查询条件（字段和值）
     * @param pageIndex 查询第几页数据（从1开始）
     * @param pageSize 每页数据条数，-1表示不分页
     * @param sortField 排序字段
     * @param sortOrder 排序顺序
     * @return
     * @throws Exception 
     * @author ${author}
     * @reviewer 
     * @createTime ${nowTime}
     */
    public ListExBO<${table.className}Ex, Void, ${table.className}Stats> get${table.className}ExListAndStats(<#rt>
        <#lt><#if foreignColumn?? >${foreignColumn.javaType} ${foreignColumn.varName}, </#if><#rt>
        <#lt><#if useRecurseQuery==true>boolean recursion, </#if><#rt>  
        <#lt>List<FilterColumn> filterColumns,
        int pageIndex, int pageSize, String sortField, String sortOrder) throws Exception{

        if(pageSize > 10000){ //默认限制返回记录数不能超过10000条
            pageSize = 10000;
        }
        if(pageSize < 1){ //底层查询时-1表示不分页，但是为了避免返回海量数据，所以这里强制返回前10000条数据。
            pageSize = 1;
            pageSize = 10000;
        }
        if(sortField == null || sortField.length() == 0){
            //默认排序字段
            sortField = "${table.idVarName}";
            sortOrder = "DESC";
        }else {
            //根据属性名获取字段名，则返回自身
            sortField = PojoManager.getColumnName(${table.className}Ex.class, sortField, sortField);
        }
        
        //将filterColumns中的ColumnName进行处理，如果值为propName，则替换为ColumnName
        FilterColumn.replaceFilterColumnName(${table.className}Ex.class, filterColumns);
             
        QueryResultAggregation<${table.className}Ex, ${table.className}Stats> ${table.beanName}List = ${table.beanName}Dao.get${table.className}ExListAndStats(<#if foreignColumn??>${foreignColumn.varName}, </#if><#if useRecurseQuery==true>recursion, </#if>filterColumns, pageIndex, pageSize, sortField, sortOrder);

        //如果需要返回更多的内容，可以基于ListExBO实现自己的子类，然后返回子类对象
        ListExBO<${table.className}Ex, Void, ${table.className}Stats> ${table.beanName}BOList = ListExBO.newInstance(${table.beanName}List.getEntities(), ${table.beanName}List.getAggregateEntities(), ${table.beanName}List.getPagination());
    <#if table.isLockByMaster>
        <#if useRecurseQuery==true>
        throw new HException("本表设置了递归查询，因此无法按主数据进行锁定（目前没有实现递归锁定）");
        </#if>
        int lockState = 0;
        <#if foreignColumn??>
        if(${foreignColumn.varName} == null || <#if foreignColumn.javaType="String">${foreignColumn.varName}.length==0<#else>${foreignColumn.varName} == 0</#if>){
            throw new HException("本功能设置了按主数据获取锁定状态，${foreignColumn.varName}不能为空");
        }
        lockState = sysDataService.getLockState(PojoManager.getTableName(${foreignTable.className}.class), null, ${foreignColumn.varName}, PojoManager.getTableName(${table.className}.class));
        <#else>
        //当没有根据主数据Id进行查询时，只能对全部检查锁定状态。如果需要按主数据进行锁定，需要在方法参数中增加主数据id，作为查询条件（不能放在filterColumns里）。
        lockState = sysDataService.getLockState(PojoManager.getTableName(${table.className}.class), null, -1L, null);
        </#if>
        ${table.beanName}BOList.setLockState(lockState);
    </#if>

        ${table.beanName}BOList.setAuthTag(AuthTag.All);//模板生成时默认设置为登录用户对此数据拥有最高权限。需要修改为根据权限系统获取权限。
        
        return ${table.beanName}BOList;
    }

    /** property **/
    @Autowired
    private ${table.className}Dao ${table.beanName}Dao;
    
    @Autowired
    private SysDataService sysDataService;
}
